package services

import (
	"com.opennews.openplatform/web_api/domain"
	"regexp"
	"strings"
)

// The public member is shared within entire application.
var CachedRequestMap []domain.RequestMap

type RequestMapService interface {
	RefreshCache()
	ExtractRoleNames(configAttribute string) []string
	ContainsAnyRoleInConfigRoleNames(roleNames []string, configRoleNames []string) bool
	ContainsAnyRoleInConfigAttribute(roleNames []string, configAttribute string) bool
}

// Returns the instance of private member, authService, which implements the RequestMapService interface.
func NewRequestMapService() RequestMapService {
	return requestMapService{}
}

// Queries and caches config request map from datasource.
func (this requestMapService) RefreshCache() {
	// This is for testing purpose only.
	// In real world you might need to query it from datasource.
	CachedRequestMap = []domain.RequestMap{
		{
			ConfigAttribute: "permitAll",
			HttpMethod:      "post",
			Url:             "/authentication/login",
		},
		{
			ConfigAttribute: "permitAll",
			HttpMethod:      "post",
			Url:             "/authentication/refresh-token",
		},
		{
			ConfigAttribute: "isFullyAuthenticated()",
			HttpMethod:      "get",
			Url:             "/user/get",
		},
		{
			ConfigAttribute: "isFullyAuthenticated",
			HttpMethod:      "get",
			Url:             "/user/*",
		},
		{
			ConfigAttribute: "isFullyAuthenticated()",
			HttpMethod:      "get",
			Url:             "/order/**",
		},
		{
			ConfigAttribute: "isFullyAuthenticated()",
			HttpMethod:      "get",
			Url:             "/user/birthday/:year/:month",
		},
		{
			ConfigAttribute: "hasAnyRole('admin')",
			HttpMethod:      "get",
			Url:             "/user/query",
		},
	}
}

// Extracts role names from request map config attribute.
// Example: hasRole('admin'), hasAnyRole('reporter', 'planner').
func (this requestMapService) ExtractRoleNames(configAttribute string) []string {
	var roleNames []string

	// This would match the role names, like 'admin' or 'reporter', 'planner'.
	matchedStrings := regexp.MustCompile(`(?:hasRole|hasAnyRole)\((.+)\)`).FindStringSubmatch(configAttribute)
	//matchedStrings := regexp.MustCompile(`\w+\((.+)\)`).FindStringSubmatch(configAttribute)

	// If match found.
	if len(matchedStrings) == 2 {
		// Removes all single quotes.
		matchedRoles := strings.Replace(matchedStrings[1], "'", "", -1)

		// Removes all double quotes.
		matchedRoles = strings.Replace(matchedRoles, "\"", "", -1)

		// Splits the string by comma.
		splitedRoles := strings.Split(matchedRoles, ",")

		// Trims space for each role name.
		for _, roleName := range splitedRoles {
			roleNames = append(roleNames, strings.TrimSpace(roleName))
		}
	}

	return roleNames
}

// Checks if configuredRoleNames contains any role in roleNames.
func (this requestMapService) ContainsAnyRoleInConfigRoleNames(roleNames []string, configRoleNames []string) bool {
	containsAnyRole := false

	for _, roleName := range roleNames {
		for _, configRoleName := range configRoleNames {
			if strings.ToLower(roleName) == strings.ToLower(configRoleName) {
				containsAnyRole = true
				break
			}
		}
	}

	return containsAnyRole
}

// Checks if security request map configAttribute contains any role in roleNames.
func (this requestMapService) ContainsAnyRoleInConfigAttribute(roleNames []string, configAttribute string) bool {
	return this.ContainsAnyRoleInConfigRoleNames(roleNames, this.ExtractRoleNames(configAttribute))
}

type requestMapService struct{}
